home *** CD-ROM | disk | FTP | other *** search
- unit uVCard;
-
- {
- *******************************************************************************
- * Descriptions: vCard object implementaton
- * $Source: /cvsroot/fma/fma/uVCard.pas,v $
- * $Locker: $
- *
- * Todo:
- * - explore source for comments
- *
- * Change Log:
- * $Log: uVCard.pas,v $
- * Revision 1.18.6.1 2004/10/14 16:43:28 z_stoichev
- * Bugfixes
- *
- * Revision 1.18 2004/07/26 12:52:17 z_stoichev
- * Unicode fixes
- *
- * Revision 1.17 2004/07/11 12:10:07 voxik
- * - Fixed soft line breaks if QP encoding is used
- *
- * Revision 1.16 2004/07/01 14:42:00 z_stoichev
- * vCard note support.
- * Bugfixes!!
- *
- * Revision 1.15 2004/05/21 14:39:13 z_stoichev
- * Fixed Contact name changes not saved
- * Fixed Display name encoding
- *
- * Revision 1.14 2004/05/19 18:34:16 z_stoichev
- * Build 0.1.0.35c
- *
- * Revision 1.13 2004/03/26 18:37:40 z_stoichev
- * Build 0.1.0.35 RC5
- *
- * Revision 1.12 2004/03/12 14:41:52 z_stoichev
- * Added vCard Grouping support (read only, ignored).
- * Added vCard Quoted-Printable Photo decoding.
- * Added vCard Unfolding support.
- * Added vCard Localy stored phone image (in file).
- * Added vCard Preffered phone number support.
- * Added vCard Agent support (nested vCards).
- * Added vCard UID (GUID) support.
- *
- *
- }
-
- interface
-
- uses Classes, SysUtils, Jpeg, RxGif, Graphics;
-
- type
- TVCard = class(TObject)
- private
- { Private declarations }
- Grouping,PropertyName: Widestring;
- sl: TStringList;
- function GetRaw: TStrings;
- procedure SetRaw(const Value: TStrings);
- procedure setProperty(Value: String);
- public
- { Public declarations }
- Name: Widestring;
- TelWork: Widestring;
- TelHome: Widestring;
- TelFax: Widestring;
- TelCell: Widestring;
- TelOther: Widestring;
- Email: Widestring;
- Title: Widestring;
- Org: Widestring;
- LUID: Widestring;
- VType: Widestring;
- Version: Widestring;
- PhotoType: Integer;
- Photo: TGraphic;
- Surname: Widestring;
- DisplayName: Widestring;
- FullName: Widestring;
- Notes: Widestring;
- TelPref: string; // H = HOME, W = Work, F = Fax, M = CELL, O = Other
- UID: string;
- ModifiedDate: TDateTime;
- constructor Create;
- destructor Destroy; override;
- procedure Clear;
- published
- property Raw: TStrings read GetRaw write SetRaw;
- end;
-
- //function GetName(Value: String;): String;
- //function GetSurname(Value: String;): String;
- function ExtractNameSurname(Value: Widestring; QP: Boolean): Widestring;
-
- function Str2QP(instr: String): String;
- function QP2Str(instr: String): String;
-
- { Warning! Next function return a new instance of stream! }
- // TODO: function Str2B64(instr: TStream): TStream;
- function B642Str(instr: TStream): TStream;
-
- const
- _Code64: string[64]=('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');
-
- implementation
-
- uses
- Unit1, TntSystem;
-
- function Str2QP(instr: String): String;
- var
- i,j,k,m,n: Integer;
- begin
- {
- Quoted-Printable lines of text must also be limited to less than 76 characters.
- The 76 characters does not include the CRLF (RFC 822) line break sequence.
- For example a multiple line LABEL property value of:
-
- 123 Winding Way
- Any Town, CA 12345
- USA
-
- Would be represented in a Quoted-Printable encoding as:
-
- LABEL;ENCODING=QUOTED-PRINTABLE:123 Winding Way=0D=0A=Any Town, CA 12345=0D=0A=USA
- }
- instr := TrimLeft(instr);
- Result := '';
- j := 0; k := Length(instr);
- for i := 1 to k do begin
- if instr[i] = '=' then begin
- Result := Result + '=' + IntToHex(Ord(instr[i]),2);
- inc(j,2);
- end
- else
- if ((instr[i] >= #32) and (instr[i] <= #126)) then
- Result := Result + instr[i]
- else begin
- Result := Result + '=' + IntToHex(Ord(instr[i]),2);
- inc(j,2);
- end;
- inc(j);
- // should we fold the line? 73 (+ max next 3) <= max 76
- if (j > 73) and (i < k) then begin
- // Folding the result into several lines is possible wherever there may be
- // linear white space (NOT simply LWSP-chars), a CRLF immediately followed
- // by at least one LWSP-char may instead be inserted.
- n := Length(Result);
- m := n;
- { find latest LWSP-char }
- while (m <> 0) and (Result[m] <> ' ') do dec(m);
- { if found insert soft line break and CRLF before it }
- if m <> 0 then begin
- Insert('=' + #13#10,Result,m);
- j := n - m + 1; // count the LWSP-char too
- end;
- end;
- end;
- end;
-
- function QP2Str(instr: String): String;
- begin
- { Remove any LWSP-char prefix and CRLF suffix chars, Realy needed??? }
- instr := Trim(instr);
- Result := '';
-
- while length(instr) > 0 do begin
- // Check for 'soft' line break
- if (instr[1] = '=') and (instr[2] <> ' ') then begin
- Result := Result + chr(StrToInt('$' + instr[2] + instr[3]));
- Delete(instr, 1, 3);
- end
- else begin
- // If 'soft' line break, just delete it
- if instr[1] <> '=' then Result := Result + instr[1];
- Delete(instr, 1, 1);
- end;
- end;
- end;
-
- function B642Str(instr: TStream): TStream;
- var
- S2: TMemoryStream;
- A1: array[1..4] of Byte;
- B1: array[1..3] of Byte;
- Byte_Ptr,Real_Bytes: Integer;
- B: Byte;
- C: Char;
- begin
- instr.Seek(0,soFromBeginning);
- S2:= TMemoryStream.Create;
- try
- Byte_Ptr:= Low(A1);
- while instr.Position < instr.Size do
- begin
- instr.ReadBuffer(C, SizeOf(C));
- if C > ' ' then
- begin
- case C of
- 'A'..'Z': B:=Ord(C)-65; {<65..90> --> <0..25>}
- 'a'..'z': B:=Ord(C)-71; {<97..122> --> <26..51>}
- '0'..'9': B:=Ord(C)+4; {<48..57> --> <52..61>}
- '+': B:=62;{43}
- '/': B:=63;{47}
- else
- {'=': }B:=64;{61}
- end;
- A1[Byte_Ptr]:= B;
- Inc(Byte_Ptr);
- if Byte_Ptr=High(A1)+1 then
- begin
- Byte_ptr:=Low(A1);
- Real_Bytes:=3;
- if A1[1]=64 then Real_Bytes:=0;
- if A1[3]=64 then
- begin
- a1[3]:=0;
- a1[4]:=0;
- real_bytes:=1;
- end;
- if a1[4]=64 then
- begin
- a1[4]:=0;
- real_bytes:=2;
- end;
- b1[1]:=a1[1]*4+(a1[2] div 16);
- b1[2]:=(a1[2] mod 16)*16+(a1[3]div 4);
- b1[3]:=(a1[3] mod 4)*64 +a1[4];
- S2.WriteBuffer(b1, real_bytes);
- end;
- end;
- end;
- finally
- result := S2;
- result.Seek(0,soFromBeginning);
- end;
- end;
-
- { TVCard }
-
- function ExtractNameSurname(Value: Widestring; QP:Boolean): Widestring;
- var
- surname,name: widestring;
- function FirstToken(var Text: Widestring): Widestring;
- var
- i: integer;
- begin
- i := Pos(';',Text);
- if i = 0 then i := Length(Text)+1;
- Result := Copy(Text,1,i-1);
- Delete(Text,1,i);
- end;
- begin
- {
- String; maximum length 18 bytes. Encapsulates the individual components
- of an objectÆs name. The property value is a concatenation of the Family
- Name (first field), Given Name (second field), Additional Names (third field),
- Name Prefix (fourth field) and Name Suffix (fifth field) strings.
-
- So we have "Family;Given;Additional;Prefix;Suffix". Please note that
- the prefix "N;QUOTED-PRINTABLE;CHARSET=ISO-8859-1:" is already removed.
-
- WARNING!!!! Note that "Additional;Prefix;Suffix" is ignored!
- }
-
- { Dako - Split data into a list was broken, so removed! }
- if QP then Value := QP2Str(Value);
-
- { Value format "Family;Given;Additional;Prefix;Suffix" }
- surname := FirstToken(Value);
- name := FirstToken(Value);
-
- { Result format "Name;Surname" i.e. "Given;Family" }
- Result := name + ';' + surname;
- end;
-
- procedure TVCard.Clear;
- begin
- Name:='';
- Surname := '';
- TelPref := '';
- TelWork:='';
- TelHome:='';
- TelFax:='';
- TelCell:='';
- Email:='';
- TelOther:='';
- Title:='';
- Org:='';
- LUID:='';
- VType:='';
- Version:='';
- DisplayName := '';
- PhotoType := 0;
- FreeAndNil(Photo);
- Grouping := '';
- PropertyName := '';
- UID := '';
- Notes := '';
- ModifiedDate := 0;
- sl.Clear;
- end;
-
- constructor TVCard.Create;
- begin
- inherited;
- sl := TStringList.Create;
- end;
-
- destructor TVCard.Destroy;
- begin
- Clear;
- sl.Free;
- inherited;
- end;
-
- function TVCard.GetRaw: TStrings;
- var
- strTemp : string;
- strN : WideString;
- i: integer;
- //tz: TTimeZoneInformation;
- begin
- sl.Clear;
- if VType = '' then
- sl.Add('BEGIN:VCARD')
- else
- sl.Add('BEGIN:' + VType);
-
- if Version = '' then
- sl.Add('VERSION:2.1')
- else
- sl.Add('VERSION:' + Version);
-
- { remove old name/surname from fullname }
- strN := FullName;
- i := Pos(';',strN);
- if i <> 0 then Delete(strN,1,i);
- i := Pos(';',strN);
- if i = 0 then i := Length(strN);
- Delete(strN,1,i);
- { add new ones to fullname }
- FullName := Surname + ';' + Name;
- if strN <> '' then
- FullName := FullName + ';' + strN;
- strN := FullName;
-
- strTemp := WideStringToUTF8(strN);
- if not Form1.FUseUTF8 or (strTemp = strN) then begin
- strTemp := Str2QP(strN);
- if strN = strTemp then
- sl.add('N:' + strN)
- else
- sl.Add('N;ENCODING=QUOTED-PRINTABLE:' + strTemp);
- end else
- sl.Add('N;CHARSET=UTF-8:' + strTemp);
-
- if DisplayName = '' then begin
- { build default 'file as' field }
- DisplayName := Name;
- if Surname <> '' then
- DisplayName := DisplayName + ' ' + Surname;
- if Name = '' then
- DisplayName := Surname;
- end;
- if DisplayName <> '' then begin
- strTemp := WideStringToUTF8(DisplayName);
- if not Form1.FUseUTF8 or (strTemp = DisplayName) then begin
- strTemp := Str2QP(DisplayName);
- if DisplayName = strTemp then
- sl.add('FN:' + DisplayName)
- else
- sl.Add('FN;ENCODING=QUOTED-PRINTABLE:' + strTemp);
- end else
- sl.Add('FN;CHARSET=UTF-8:' + strTemp);
- end;
-
- if Notes <> '' then begin
- strTemp := WideStringToUTF8(Notes);
- if not Form1.FUseUTF8 or (strTemp = Notes) or (Pos(#13,Notes) <> 0) then begin
- strTemp := Str2QP(Notes);
- if Notes = strTemp then
- sl.add('NOTE:' + Notes)
- else
- sl.Add('NOTE;ENCODING=QUOTED-PRINTABLE:' + strTemp);
- end else
- sl.Add('NOTE;CHARSET=UTF-8:' + strTemp);
- end;
-
- if Title <> '' then begin
- strTemp := WideStringToUTF8(Title);
- if not Form1.FUseUTF8 or (strTemp = Title) then begin
- strTemp := Str2QP(Title);
- if Title = strTemp then
- sl.add('TITLE:' + Title)
- else
- sl.Add('TITLE;ENCODING=QUOTED-PRINTABLE:' + strTemp);
- end else
- sl.Add('TITLE;CHARSET=UTF-8:' + strTemp);
- end;
-
- if Org <> '' then begin
- strTemp := WideStringToUTF8(Org);
- if not Form1.FUseUTF8 or (strTemp = Org) then begin
- strTemp := Str2QP(Org);
- if Org = strTemp then
- sl.add('ORG:' + Org)
- else
- sl.Add('ORG;ENCODING=QUOTED-PRINTABLE:' + strTemp);
- end else
- sl.Add('ORG;CHARSET=UTF-8:' + strTemp);
- end;
-
- if Email <> '' then begin
- sl.add('EMAIL;INTERNET;PREF:' + Email)
- end;
-
- if TelHome <> '' then begin
- if TelPref <> 'H' then
- sl.add('TEL;HOME:' + TelHome)
- else
- sl.add('TEL;HOME;PREF:' + TelHome)
- end;
- if TelWork <> '' then begin
- if TelPref <> 'W' then
- sl.add('TEL;WORK:' + TelWork)
- else
- sl.add('TEL;WORK;PREF:' + TelWork)
- end;
- if TelCell <> '' then begin
- if TelPref <> 'M' then
- sl.add('TEL;CELL:' + TelCell)
- else
- sl.add('TEL;CELL;PREF:' + TelCell)
- end;
- if TelFax <> '' then begin
- if TelPref <> 'F' then
- sl.add('TEL;FAX:' + TelFax)
- else
- sl.add('TEL;FAX;PREF:' + TelFax)
- end;
- if TelOther <> '' then begin
- if TelPref <> 'O' then
- sl.add('TEL:' + TelOther)
- else
- sl.add('TEL;PREF:' + TelOther)
- end;
-
- // TODO: Optional, add support for photo image
-
- if UID <> '' then begin
- sl.add('UID:' + UID)
- end;
-
- if LUID <> '' then begin
- sl.add('X-IRMC-LUID:' + LUID)
- end;
-
- // REV:20040701T095208Z
- //GetTimeZoneInformation(tz);
- //sl.add('REV:'+FormatDateTime('yyyymmdd"T"hhnn',ModifiedDate)+Format('%.2dZ',[-tz.Bias div 15]));
- sl.add('REV:'+FormatDateTime('yyyymmdd"T"hhnnss"Z"',ModifiedDate));
-
- if VType = '' then
- sl.Add('END:VCARD')
- else
- sl.Add('END:' + VType);
- Result := sl;
- end;
-
- procedure TVCard.setProperty(Value: String);
- const
- ValueRaw: String = '';
- function IsField(FName,Value: string): boolean;
- var
- i,j: integer;
- begin
- i := Length(FName);
- j := Length(Value);
- Result := (Pos(FName,Value) = 1) and ((i = j) or
- (Value[i+1] in [';',':']) or (FName[i] in [';',':']));
- end;
- var
- str,grp,grpdescr,nme,nmedescr: Widestring;
- i,j: integer;
- procedure CheckUTFs(var Value: String);
- begin
- if Pos('UTF-7',Value) <> 0 then
- Value := UTF7ToWideString(Value)
- else
- if Form1.FUseUTF8 and (Pos('UTF-8',Value) <> 0) then
- Value := UTF8Decode(Value);
- end;
- procedure ProcessRaw(var Value: String);
- begin
- { find end pos+1 of propery name }
- i := Pos(';',Value);
- j := Pos(':',Value);
- if i < j then j := i;
- { find start pos-1 of propery name }
- i := Pos('.',Value);
- if i > j then i := 0;
- { get grouping name, if any }
- grp := UpperCase(Copy(Value,1,i-1));
- grpdescr := Copy(Value,i+1,length(Value));
- { get property name }
- nme := UpperCase(Copy(Value,i+1,j-i-1));
- { remove grouping, leave property name at the begining of Value }
- Delete(Value,1,i);
- { get full name (with desctiptions) }
- i := Pos(':',Value);
- nmedescr := Copy(Value,1,i-1);
- { keep values }
- if Grouping = grp then begin
- {
- The grouping of a comment property with a telephone property is shown in the following example:
-
- A.TEL;HOME:+1-213-555-1234
- A.NOTE:This is my vacation home
-
- In this case PropertyName="TEL;HOME", Grouping="A", nme="NOTE", grpdescr="This is my vacation home"
- }
- // TODO: use grouping description somehow
- end
- else
- Grouping := grp;
- PropertyName := nmedescr;
-
- if IsField('BEGIN',Value) then
- Vtype := copy(Value, pos(':', Value) + 1, length(Value));
- if Pos('VERSION',Value) = 1 then
- Version := copy(Value, pos(':', Value) + 1, length(Value));
-
- if IsField('N',Value) then begin
- CheckUTFs(Value);
- FullName := copy(Value, pos(':', Value) + 1, length(Value));
- str := ExtractNameSurname(FullName, Pos('QUOTED-PRINTABLE',nmedescr) <> 0);
- Name := Copy(str, 1,Pos(';', str) - 1);
- Surname := Copy(str,Pos(';', str) + 1, length(str));
- end;
-
- if IsField('FN',Value) then begin
- CheckUTFs(Value);
- if Pos('QUOTED-PRINTABLE',nmedescr) <> 0 then
- DisplayName := QP2Str(copy(Value, pos(':', Value) + 1, length(Value)))
- else
- DisplayName := copy(Value, pos(':', Value) + 1, length(Value));
- end;
-
- if IsField('TEL;WORK',Value) then begin
- if Pos('PREF',nmedescr) <> 0 then TelPref := 'W';
- TelWork := copy(Value, pos(':', Value) + 1, length(Value));
- end;
- if IsField('TEL;HOME',Value) then begin
- if Pos('PREF',nmedescr) <> 0 then TelPref := 'H';
- TelHome := copy(Value, pos(':', Value) + 1, length(Value));
- end;
- if IsField('TEL;FAX',Value) then begin
- if Pos('PREF',nmedescr) <> 0 then TelPref := 'F';
- TelFax := copy(Value, pos(':', Value) + 1, length(Value));
- end;
- if IsField('TEL;CELL',Value) then begin
- if Pos('PREF',nmedescr) <> 0 then TelPref := 'M';
- TelCell := copy(Value, pos(':', Value) + 1, length(Value));
- end;
- { phone type not specified }
- if IsField('TEL:',Value) then begin
- if Pos('PREF',nmedescr) <> 0 then TelPref := 'O';
- TelOther := copy(Value, pos(':', Value) + 1, length(Value));
- end;
-
- if IsField('TITLE',Value) then begin
- CheckUTFs(Value);
- if Pos('QUOTED-PRINTABLE',nmedescr) <> 0 then
- Title := QP2Str(copy(Value, pos(':', Value) + 1, length(Value)))
- else
- Title := copy(Value, pos(':', Value) + 1, length(Value));
- end;
-
- if IsField('ORG',Value) then begin
- CheckUTFs(Value);
- if Pos('QUOTED-PRINTABLE',nmedescr) <> 0 then
- Org := QP2Str(copy(Value, pos(':', Value) + 1, length(Value)))
- else
- Org := copy(Value, pos(':', Value) + 1, length(Value));
- end;
-
- if IsField('EMAIL',Value) then
- // TODO: Add support for several e-mail addresses
- if (Pos('INTERNET',nmedescr) <> 0) and (Pos('PREF',nmedescr) <> 0) then
- Email := copy(Value, pos(':', Value) + 1, length(Value));
-
- if IsField('NOTE',Value) then begin
- CheckUTFs(Value);
- if Pos('QUOTED-PRINTABLE',nmedescr) <> 0 then
- Notes := QP2Str(copy(Value, pos(':', Value) + 1, length(Value)))
- else
- Notes := copy(Value, pos(':', Value) + 1, length(Value));
- end;
-
- { TODO: Add ModifiedDate support }
- // REV:20040701T095208Z
-
- if IsField('UID',Value) then
- UID := copy(Value, pos(':', Value) + 1, length(Value));
-
- if IsField('X-IRMC-LUID',Value) then
- LUID := copy(Value, pos(':', Value) + 1, length(Value));
-
- Value := '';
- end;
- begin
- { unfold a vCard raw, if needed }
- if Value = '' then begin
- if ValueRaw <> '' then ProcessRaw(ValueRaw); // this will clear ValueRaw
- end
- else begin
- if Value[1] = ' ' then begin
- {
- Individual lines within the vCard data stream are delimited by the (RFC 822) line break,
- which is a CRLF sequence (ASCII decimal 13, followed by ASCII decimal 10). Long lines
- of text can be split into a multiple-line representation using the RFC 822 "folding"
- technique. That is, wherever there may be linear white space (NOT simply LWSP-chars),
- a CRLF immediately followed by at least one LWSP-char may instead be inserted.
- For example the line:
-
- NOTE:This is a very long description that exists on a long line.
-
- Can be represented as:
-
- NOTE:This is a very long description
- that exists on a long line.
-
- The process of moving from this folded multiple-line representation of a property definition
- to its single line representation is called "unfolding". Unfolding is accomplished by regarding
- CRLF immediately followed by a LWSP-char as equivalent to the LWSP-char.
- }
- ValueRaw := ValueRaw + ' ' + TrimLeft(Value);
- exit;
- end
- else begin
- if ValueRaw <> '' then ProcessRaw(ValueRaw);
- ValueRaw := Value;
- end;
- end;
- end;
-
- procedure TVCard.SetRaw(const Value: TStrings);
- var
- i: Integer;
- s: string;
- isAgent,isBody,isPhoto,isPhotoQP: boolean;
- PhotoStream: TStream;
- stream: TStream;
- begin
- Clear;
- isAgent := False;
- isBody := False;
- isPhoto := False;
- isPhotoQP := False; // default is BASE64
- PhotoStream := nil;
-
- { Process incoming data }
- for i := 0 to Value.Count - 1 do begin
- { check for nested vCard (Agent) into specified vCard }
- if pos('AGENT', Value.Strings[i]) = 1 then isAgent := True;
- if isAgent then begin
- {
- This property specifies information about another person who will act on behalf of the vCard object.
- Typically this would be an area administrator, assistant, or secretary for the individual. A key
- characteristic of the Agent property is that it represents somebody or something which is separately
- addressable.
- }
- // ignore Agent vCard!
- // SetProperty(Value.Strings[i]);
- if pos('END', Value.Strings[i]) = 1 then isAgent := False;
- Continue;
- end;
-
- if pos('BEGIN', Value.Strings[i]) = 1 then isBody := True
- else if pos('END', Value.Strings[i]) = 1 then isBody := False
- else if pos('PHOTO', Value.Strings[i]) = 1 then isPhoto :=True
- else if Value.Strings[i] = '' then
- isPhoto := False;
-
- if isBody then begin
- if isPhoto then begin
- if Pos('PHOTO', Value.Strings[i]) = 1 then begin
- { check image encoding }
- if Pos('TYPE=GIF', Value.Strings[i]) <> 0 then
- PhotoType := 1
- else if Pos('TYPE=JPEG', Value.Strings[i]) <> 0 then
- PhotoType := 2;
- {
- In the case of the vCard being transported within a MIME email message, the property value
- can be specified as being located in a separate MIME entity with the "Content-ID" value, or
- "CID" for short. In this case, the property value is the Content-ID for the MIME entity
- containing the property value. In addition, the property value can be specified as being
- located out on the network within some Internet resource with the "URL" value. In this case,
- the property value is the Uniform Resource Locator for the Internet resource containing the
- property value. The following specifies a value not located inline with the vCard but out
- in the Internet:
-
- PHOTO;VALUE=URL;TYPE=GIF:http://www.abc.com/dir_photos/my_photo.gif
- SOUND;VALUE=CONTENT-ID:<jsmith.part3.960817T083000.xyzMail@host1.com
- }
- if Pos('VALUE=URL', Value.Strings[i]) <> 0 then begin
- s := copy(Value.Strings[i], pos(':', Value.Strings[i]) + 1, length(Value.Strings[i]));
- if Pos('file:///',s) = 1 then begin
- Delete(s,1,8);
- try
- PhotoStream := TFileStream.Create(s,fmOpenRead);
- except
- PhotoType := 0; // ignore image on error (file not found etc.)
- end;
- end
- else
- // TODO: Add support for vCard external images (http)
- PhotoType := 0; // ignore image - not implemented
- end
- else if Pos('VALUE=CONTENT-ID', Value.Strings[i]) <> 0 then begin
- // TODO: Add support for vCard MIME content-id
- PhotoType := 0; // ignore image - not implemented
- end
- else begin
- { begin collecting image data... }
- isPhotoQP := Pos('QUOTED-PRINTABLE',Value.Strings[i]) <> 0;
- sl.Add(Trim(copy(Value.Strings[i], pos(':', Value.Strings[i]) + 1, length(Value.Strings[i]))));
- end;
- end
- else begin
- { ...adding more image data }
- sl.add(Trim(Value.Strings[i]));
- end;
- end
- else
- SetProperty(Value.Strings[i]);
- end;
- end;
- { Flush any unfolded vCard raw }
- SetProperty('');
-
- { check if photo image exists }
- stream := TMemoryStream.Create;
- try
- sl.SaveToStream(stream);
- sl.Clear;
- if (PhotoStream = nil) and (PhotoType <> 0) then begin
- if isPhotoQP then begin
- sl.Text := QP2Str(StringReplace(sl.Text,#13#10,'',[rfReplaceAll]));
- PhotoStream := TMemoryStream.Create;
- sl.SaveToStream(PhotoStream);
- end
- else
- PhotoStream := b642str(stream); // this will create a stream instance
- end;
- try
- case PhotoType of
- 1: begin
- Photo := TGIFImage.Create;
- Photo.LoadFromStream(PhotoStream)
- end;
- 2: begin
- Photo := TJPEGImage.Create;
- Photo.LoadFromStream(PhotoStream)
- end;
- end;
- finally
- PhotoStream.Free;
- end;
- finally
- stream.Free;
- end;
- end;
-
- end.
-